// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr>
// Copyright (C) 2009 Hauke Heibel <hauke.heibel@googlemail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef EIGEN_STDDEQUE_H
#define EIGEN_STDDEQUE_H

#include "details.h"

/**
 * This section contains a convenience MACRO which allows an easy specialization of
 * std::deque such that for data types with alignment issues the correct allocator
 * is used automatically.
 */
#define EIGEN_DEFINE_STL_DEQUE_SPECIALIZATION(...)                                                                     \
	namespace std {                                                                                                    \
	template<>                                                                                                         \
	class deque<__VA_ARGS__, std::allocator<__VA_ARGS__>>                                                              \
		: public deque<__VA_ARGS__, EIGEN_ALIGNED_ALLOCATOR<__VA_ARGS__>>                                              \
	{                                                                                                                  \
		typedef deque<__VA_ARGS__, EIGEN_ALIGNED_ALLOCATOR<__VA_ARGS__>> deque_base;                                   \
                                                                                                                       \
	  public:                                                                                                          \
		typedef __VA_ARGS__ value_type;                                                                                \
		typedef deque_base::allocator_type allocator_type;                                                             \
		typedef deque_base::size_type size_type;                                                                       \
		typedef deque_base::iterator iterator;                                                                         \
		explicit deque(const allocator_type& a = allocator_type())                                                     \
			: deque_base(a)                                                                                            \
		{                                                                                                              \
		}                                                                                                              \
		template<typename InputIterator>                                                                               \
		deque(InputIterator first, InputIterator last, const allocator_type& a = allocator_type())                     \
			: deque_base(first, last, a)                                                                               \
		{                                                                                                              \
		}                                                                                                              \
		deque(const deque& c)                                                                                          \
			: deque_base(c)                                                                                            \
		{                                                                                                              \
		}                                                                                                              \
		explicit deque(size_type num, const value_type& val = value_type())                                            \
			: deque_base(num, val)                                                                                     \
		{                                                                                                              \
		}                                                                                                              \
		deque(iterator start_, iterator end_)                                                                          \
			: deque_base(start_, end_)                                                                                 \
		{                                                                                                              \
		}                                                                                                              \
		deque& operator=(const deque& x)                                                                               \
		{                                                                                                              \
			deque_base::operator=(x);                                                                                  \
			return *this;                                                                                              \
		}                                                                                                              \
	};                                                                                                                 \
	}

// check whether we really need the std::deque specialization
#if !EIGEN_HAS_CXX11_CONTAINERS &&                                                                                     \
	!(defined(_GLIBCXX_DEQUE) &&                                                                                       \
	  (!EIGEN_GNUC_AT_LEAST(                                                                                           \
		  4, 1))) /* Note that before gcc-4.1 we already have: std::deque::resize(size_type,const T&). */

namespace std {

#define EIGEN_STD_DEQUE_SPECIALIZATION_BODY                                                                            \
  public:                                                                                                              \
	typedef T value_type;                                                                                              \
	typedef typename deque_base::allocator_type allocator_type;                                                        \
	typedef typename deque_base::size_type size_type;                                                                  \
	typedef typename deque_base::iterator iterator;                                                                    \
	typedef typename deque_base::const_iterator const_iterator;                                                        \
	explicit deque(const allocator_type& a = allocator_type())                                                         \
		: deque_base(a)                                                                                                \
	{                                                                                                                  \
	}                                                                                                                  \
	template<typename InputIterator>                                                                                   \
	deque(InputIterator first, InputIterator last, const allocator_type& a = allocator_type())                         \
		: deque_base(first, last, a)                                                                                   \
	{                                                                                                                  \
	}                                                                                                                  \
	deque(const deque& c)                                                                                              \
		: deque_base(c)                                                                                                \
	{                                                                                                                  \
	}                                                                                                                  \
	explicit deque(size_type num, const value_type& val = value_type())                                                \
		: deque_base(num, val)                                                                                         \
	{                                                                                                                  \
	}                                                                                                                  \
	deque(iterator start_, iterator end_)                                                                              \
		: deque_base(start_, end_)                                                                                     \
	{                                                                                                                  \
	}                                                                                                                  \
	deque& operator=(const deque& x)                                                                                   \
	{                                                                                                                  \
		deque_base::operator=(x);                                                                                      \
		return *this;                                                                                                  \
	}

template<typename T>
class deque<T, EIGEN_ALIGNED_ALLOCATOR<T>>
	: public deque<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T),
				   Eigen::aligned_allocator_indirection<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T)>>
{
	typedef deque<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T),
				  Eigen::aligned_allocator_indirection<EIGEN_WORKAROUND_MSVC_STL_SUPPORT(T)>>
		deque_base;
	EIGEN_STD_DEQUE_SPECIALIZATION_BODY

	void resize(size_type new_size) { resize(new_size, T()); }

#if defined(_DEQUE_)
	// workaround MSVC std::deque implementation
	void resize(size_type new_size, const value_type& x)
	{
		if (deque_base::size() < new_size)
			deque_base::_Insert_n(deque_base::end(), new_size - deque_base::size(), x);
		else if (new_size < deque_base::size())
			deque_base::erase(deque_base::begin() + new_size, deque_base::end());
	}
	void push_back(const value_type& x) { deque_base::push_back(x); }
	void push_front(const value_type& x) { deque_base::push_front(x); }
	using deque_base::insert;
	iterator insert(const_iterator position, const value_type& x) { return deque_base::insert(position, x); }
	void insert(const_iterator position, size_type new_size, const value_type& x)
	{
		deque_base::insert(position, new_size, x);
	}
#else
	// default implementation which should always work.
	void resize(size_type new_size, const value_type& x)
	{
		if (new_size < deque_base::size())
			deque_base::erase(deque_base::begin() + new_size, deque_base::end());
		else if (new_size > deque_base::size())
			deque_base::insert(deque_base::end(), new_size - deque_base::size(), x);
	}
#endif
};
}

#endif // check whether specialization is actually required

#endif // EIGEN_STDDEQUE_H
